#include "net_server.h"
#include "log.h"

namespace edm{
namespace network{

net_server_t::net_server_t(const std::string& host, const std::string port, unsigned int concurrency):
        m_host(host), m_port(port), m_concurrency(concurrency),
        m_started(false),m_callback_handler(0),m_callback_error(0),m_callback_connect(0),
        m_io_work(new boost::asio::io_service::work(m_io_service)),
        m_acceptor(m_io_service),m_new_connection(new connection_t(m_io_service))
{
    logtrace((MYNET, "net_server_t::net_server_t <%s:%s>", m_host.c_str(), m_port.c_str()));
}

net_server_t::~net_server_t()
{
    logtrace((MYNET, "net_server_t::~net_server_t <%s:%s> begin ...", m_host.c_str(), m_port.c_str()));
    if(m_started) stop();
    logtrace((MYNET, "net_server_t::~net_server_t <%s:%s> end ok.", m_host.c_str(), m_port.c_str()));
}

//! start network server
int net_server_t::start(callback_handler_t cb, callback_error_t cb_err, callback_connect_t cb_connect)
{
    logtrace((MYNET, "net_server_t::start <%s:%s> begin ...", m_host.c_str(), m_port.c_str()));
    if(m_started)
    {
        logwarn((MYNET, "net_server_t::start <%s:%s> already started", m_host.c_str(), m_port.c_str()));
        return 0;
    }
    m_callback_handler = cb;
    m_callback_error = cb_err;
    m_callback_connect = cb_connect;
    
    //! start listenning
    boost::asio::ip::tcp::resolver resolver(m_io_service);
    boost::asio::ip::tcp::resolver::query query(m_host, m_port);
    boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
    m_acceptor.open(endpoint.protocol());
    m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
    m_acceptor.bind(endpoint);
    m_acceptor.listen();
    m_acceptor.async_accept(m_new_connection->socket(),
                            boost::bind(&net_server_t::handle_accept, this,
                            boost::asio::placeholders::error));
    
    //! start threads
    for(unsigned int i = 0; i < m_concurrency; i++)
    {
        m_thread_group.create_thread(boost::bind(&boost::asio::io_service::run, 
                                           &m_io_service));
    }
    //! set flag
    m_started = true;
    
    logtrace((MYNET, "net_server_t::start <%s:%s> end ok.", m_host.c_str(), m_port.c_str()));
    return 0;
}

//! stop network server
int net_server_t::stop()
{
    logtrace((MYNET, "net_server_t::start <%s:%s> begin ...", m_host.c_str(), m_port.c_str()));
    if(!m_started)
    {
        logwarn((MYNET, "net_server_t::start <%s:%s> already stopped", m_host.c_str(), m_port.c_str()));
        return 0;
    }
    m_acceptor.close();
    m_io_work.reset();
    m_thread_group.join_all();
    m_started = false;
    logtrace((MYNET, "net_server_t::start <%s:%s> end ok.", m_host.c_str(), m_port.c_str()));
    return 0;
}

void net_server_t::handle_accept(const boost::system::error_code& error)
{
    logtrace((MYNET, "net_server_t::handle_accept <%s:%s> begin ...", m_host.c_str(), m_port.c_str()));
    if(error)
        logwarn((MYNET, "connection_t::handle_accept failed:<%s>", error.message().c_str()));
    else
        m_new_connection->start(m_callback_handler, m_callback_error, m_callback_connect);
    m_new_connection.reset(new connection_t(m_io_service));
    m_acceptor.async_accept(m_new_connection->socket(),
                            boost::bind(&net_server_t::handle_accept, this,
                            boost::asio::placeholders::error));
    logtrace((MYNET, "net_server_t::handle_accept <%s:%s> end ok.", m_host.c_str(), m_port.c_str()));
}

}}